package com.heima.tanhua.aop;

// AOP的前置依赖
//<dependency>
//<groupId>org.aspectj</groupId>
//<artifactId>aspectjweaver</artifactId>
//</dependency>

import com.heima.tanhua.annotation.Log;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.web.bind.annotation.GetMapping;

import java.lang.reflect.Method;

// Aspect 声明当前类是一个AOP切面
@Aspect
public class LogAspect {

    // 切入点
    // execution表达式包含以下几个部分
    // 1. 修饰符
    // 2. 方法返回值
    // 3. 包名
    // 4. 类名
    // 5. 方法名
    // 6. (..)代表任意多个参数，(*)任意单个参数
    // 每个部分之间用.号隔开
    // 每个部分可以用*号代替，代表任意字符，或者不限
    @Pointcut("execution(public * com.heima.tanhua.service.*.*(..))")
    public void pt() {

    }

    // 通知（一般来说首选环绕通知）
    // @Around的参数，是对应的切入点方法
    @Around("pt()")

    // 方法参数，代表的是被拦截方法对象
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        // 1. 如果想在被拦截方法之前，执行相关代码，你就放在这句话之后，下一句注释之前

        // 假设，想在AOP里面进行反射，找到当前方法是否带有某个注解，需要以下几步
        // 1. 获取当前被拦截类的字节码对象
        Class<?> aClass = pjp.getTarget().getClass();

        // 2. 获取当前被拦截的方法名
        String name = pjp.getSignature().getName();

        // 2.1 方法入参列表
        Object[] args = pjp.getArgs();

        // 2. 在通知方法内，一定要手动调用一次pjp的执行方法，然后把返回值返回出去
        // 这个就是方法的返回值
        Object proceed = pjp.proceed();
        // 3. 如果想在被拦截方法之后，执行相关代码，你就在这句注释之后，return之前

        return proceed;
    }

    // aClass 是当前被拦截类的对象
    // name 是当前被拦截方法名
    // args 是当前被拦截方法入参
    // returnValue 是当前被拦截方法返回值
    private void checkAnnotation(Class<?> aClass, String name, Object[] args, Object returnValue) {
        // (额外的拓展（如何判断当前方法是否有某个注解？）根据方法名，获取方法的具体信息
        Method[] methods = aClass.getMethods();
        for (Method method : methods) {

            // 这个是当前被拦截的方法信息
            // 4. 调用某个方法，来判断当前方法是否包含某个注解
            if(name.equals(method.getName())) {
                // 就可以判断，当前被拦截方法，是否带有Log这个注解
                boolean annotationPresent = method.isAnnotationPresent(Log.class);

                // 链路跟踪

                // 获取当前方法的入参，出参，方法名

                // 入参 -> args
                // 出参 -> proceed
                // 方法名 -> name

                // 把这些信息存储到数据库中，并且记录时间
            }
        }
    }
}
